package com.tools.controller;

import com.tools.constants.Constant;
import com.tools.entity.BaseContent;
import com.tools.entity.ControllerContent;
import com.tools.entity.DaoContent;
import com.tools.entity.DatabaseContent;
import com.tools.entity.EntityContent;
import com.tools.entity.GeneratorContent;
import com.tools.entity.ServiceContent;
import com.tools.entity.TableAttributeEntity;
import com.tools.entity.TableContent;
import com.tools.model.BaseModel;
import com.tools.model.ControllerModel;
import com.tools.model.DaoModel;
import com.tools.model.DataBaseModel;
import com.tools.model.EntityModel;
import com.tools.model.HistoryModel;
import com.tools.model.ServiceModel;
import com.tools.utils.AlertUtil;
import com.tools.utils.ConverterUtil;
import com.tools.utils.CreateFileUtil;
import com.tools.utils.DBUtil;
import com.tools.utils.SqlLiteUtil;
import com.tools.utils.StringUtil;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.control.cell.TextFieldTreeCell;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.stage.DirectoryChooser;
import javafx.util.Callback;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import java.awt.*;
import java.io.File;
import java.net.URL;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.ResourceBundle;

/**
 * LeftTreeController
 *
 * @author < a href=" ">ZhangYang</ a>
 * @version v1.0.01
 * @date 2018-07-25 11:38
 */
public class DataBaseController extends BaseController {
  private static final Logger LOG = Logger.getLogger(DataBaseController.class);

  @FXML
  private Button newJoin;
  @FXML
  private Button template;

  @FXML
  private TreeView<String> databaseTree;
  /**
   * 保存路径
   */
  @FXML
  private TextField storagePath;
  /**
   * 表前缀
   */
  @FXML
  private TextField tablePrefix;
  /**
   * 包前缀路径
   */
  @FXML
  private TextField pkgPrefix;
  @FXML
  private TextField module;
  @FXML
  private TextField author;
  @FXML
  private TextField version;
  @FXML
  private TextArea tableText;
  @FXML
  private Button allGenerated;
  @FXML
  private Button perGenerated;
  @FXML
  private Button saveConfig;
  /** 字符编码格式 */
  @FXML
  private ComboBox<String> cboCodeFormat;
  /** entity模版选择 **/
  @FXML
  private ComboBox<String> entityComboBox;
  /** dao模版选择 **/
  @FXML
  private ComboBox<String> daoComboBox;
  /** service模版选择 **/
  @FXML
  private ComboBox<String> serviceComboBox;
  /** controller模版选择 **/
  @FXML
  private ComboBox<String> controllerComboBox;
  /** 提示进度条 **/
  @FXML
  private ProgressBar probCreateAll;
  /** 提示文字进度条 */
  @FXML
  private Label lblRunCreateAllTips;

  private List<DataBaseModel> dataBaseModels;

  MenuItem openConn;
  MenuItem delConn;
  MenuItem closeConn;
  List<String> text;
  List<String> tables;

  DataBaseModel dataBaseModel;

  public DataBaseController() throws Exception {
    dataBaseModels = SqlLiteUtil.getDatabaseModels();
  }

  @Override
  public void initialize(URL location, ResourceBundle resources) {
    /** 选择框初始化 **/
    cboCodeFormat.setEditable(false);
    cboCodeFormat.getItems().addAll("UTF-8", "GBK", "UTF-16", "UTF-32", "GB2312", "GB18030", "ISO-8859-1");

    entityComboBox.setEditable(false);
    String[] e = Paths.get(Constant.TEMPLATE_NAME_ENTITY).toFile().list();
    for (int i = 0; i < e.length; i++) {
      entityComboBox.getItems().add(e[i].split("\\.")[0]);
    }


    daoComboBox.setEditable(false);
    String[] d = Paths.get(Constant.TEMPLATE_NAME_DAO).toFile().list();
    for (int i = 0; i < d.length; i++) {
      daoComboBox.getItems().add(d[i].split("\\.")[0]);
    }

    serviceComboBox.setEditable(false);
    String[] s = Paths.get(Constant.TEMPLATE_NAME_SERVICE).toFile().list();
    for (int i = 0; i < s.length; i++) {
      serviceComboBox.getItems().add(s[i].split("\\.")[0]);
    }

    controllerComboBox.setEditable(false);
    String[] c = Paths.get(Constant.TEMPLATE_NAME_CONTROLLER).toFile().list();
    for (int i = 0; i < c.length; i++) {
      controllerComboBox.getItems().add(c[0].split("\\.")[0]);
    }

    initTVDataBase();
    loadTVDataBase();

    /** 打开新建连接窗口 **/
    newJoin.setOnAction(event -> {
      try {
        ConnectController controller = (ConnectController) loadFXMLPage(
          Constant.STRING_TITLE_ADD_CONN, FXMLPage.ADDPAGE, false, false, false);

        controller.showDialogStage();
      } catch (Exception e1) {
        e1.printStackTrace();
      }
    });

    /** 打开模版设置窗口 **/
    template.setOnAction(event -> {
      try {
        TemplateController controller = (TemplateController) loadFXMLPage(
          Constant.STRING_TITLE_TEMPLATE, FXMLPage.TEMPPAGE, false, false);
        controller.setPrimaryStage(this.getPrimaryStage());
        controller.showDialogStage();
      } catch (Exception e1) {
        e1.printStackTrace();
      }
    });

  }

  public void initTVDataBase() {
    databaseTree.setShowRoot(false);
    databaseTree.setRoot(new TreeItem<>());
    Callback<TreeView<String>, TreeCell<String>> defaultCellFactory = TextFieldTreeCell.forTreeView();
    databaseTree.setCellFactory((TreeView<String> tv) -> {
      TreeCell<String> cell = defaultCellFactory.call(tv);
      cell.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> {
        int level = databaseTree.getTreeItemLevel(cell.getTreeItem());
        TreeCell<String> treeCell = (TreeCell<String>) event.getSource();
        TreeItem<String> treeItem = treeCell.getTreeItem();
        final ContextMenu contextMenu = new ContextMenu();

        /** 构建数据表 **/
        if (level == 1) {
          dataBaseModel = (DataBaseModel) treeItem.getGraphic().getUserData();
          /** 构建右键菜单 **/

          openConn = new MenuItem("打开连接");
          openConn.setOnAction(event1 -> createCell(dataBaseModel, cell));

          closeConn = new MenuItem("关闭连接");
          closeConn.setOnAction(event1 -> {
            tableText();
            allGenerated.setDisable(true);
            perGenerated.setDisable(true);
            saveConfig.setDisable(true);
            treeItem.getChildren().clear();
          });

          delConn = new MenuItem("删除连接");
          delConn.setOnAction(event1 -> {
            if (!AlertUtil.showConfirmAlert("确定删除该连接吗")) {
              return;
            }
            try {
              SqlLiteUtil.deleteDatabaseConfig(dataBaseModel.getConnName());
              allGenerated.setDisable(true);
              perGenerated.setDisable(true);
              saveConfig.setDisable(true);
              this.initTVDataBase();
              this.loadTVDataBase();
            } catch (Exception e) {
              AlertUtil.showErrorAlert("删除数据库连接失败: " + e.getMessage());
              LOG.error("删除数据库连接失败: " + e.getMessage());
            }
          });

          contextMenu.getItems().addAll(openConn, closeConn, delConn);
          cell.setContextMenu(contextMenu);
        } else {
          if (null != treeCell.getTreeItem()) {
            String tableName = treeCell.getTreeItem().getValue();
          }
        }

        /** 双击加载所有表 **/
        if (event.getClickCount() == 2) {
          if (treeItem == null) {
            return;
          }
          treeItem.setExpanded(true);
          if (level == 1) {
            dataBaseModel = (DataBaseModel) treeItem.getGraphic().getUserData();
            createCell(dataBaseModel, cell);
          } else {
            String tableName = treeCell.getTreeItem().getValue();
            if (text.indexOf(tableName) == -1) {
              text.add(tableName);
              ImageView imageView = new ImageView("image/table1.png");
              imageView.setFitHeight(14);
              imageView.setFitWidth(14);
              cell.setGraphic(imageView);
            } else {
              text.remove(tableName);
              ImageView imageView = new ImageView("image/table.png");
              imageView.setFitHeight(14);
              imageView.setFitWidth(14);
              cell.setGraphic(imageView);
            }
            tableText.setWrapText(true);
            String string = "";
            for (String s : text) {
              string += s + " | ";
            }
            tableText.setText(string.length() == 0 ? "" : string.substring(0, string.length() - 2));
          }
        }

      });
      return cell;
    });

  }

  public void createCell(DataBaseModel selectedConfig, TreeCell<String> cell) {
    try {
      tableText();
      allGenerated.setDisable(false);
      perGenerated.setDisable(false);
      saveConfig.setDisable(false);
      tables = DBUtil.getTableNames(selectedConfig);
      if (tables != null && tables.size() > 0) {
        ObservableList<TreeItem<String>> children = cell.getTreeItem().getChildren();
        children.clear();
        // 获得树节点
        for (String tableName : tables) {
          TreeItem<String> newTreeItem = new TreeItem<>();
          ImageView imageView = new ImageView("image/table.png");
          imageView.setFitHeight(14);
          imageView.setFitWidth(14);
          newTreeItem.setGraphic(imageView);
          newTreeItem.setValue(tableName);
          children.add(newTreeItem);
        }
      }
      BaseModel baseConfig = SqlLiteUtil.getBaseConfig(cell.getTreeItem().getValue());

      if (null != baseConfig) {
        storagePath.setText(baseConfig.getStoragePath());
        pkgPrefix.setText(baseConfig.getPkgPrefix());
        tablePrefix.setText(baseConfig.getTablePrefix());
        module.setText(baseConfig.getModule());
        author.setText(baseConfig.getAuthor());
        version.setText(baseConfig.getVersion());
        cboCodeFormat.setValue(baseConfig.getEncoding());
        entityComboBox.setValue(baseConfig.getEntityTemp());
        daoComboBox.setValue(baseConfig.getDaoTemp());
        serviceComboBox.setValue(baseConfig.getServiceTemp());
        controllerComboBox.setValue(baseConfig.getControllerTemp());
      }
    } catch (Exception e) {
      AlertUtil.showErrorAlert(e.getMessage());
      LOG.error(e.getMessage());
    }
  }

  public void loadTVDataBase() {
    try {
      TreeItem<String> rootTreeItem = databaseTree.getRoot();
      rootTreeItem.getChildren().clear();
      dataBaseModels = SqlLiteUtil.getDatabaseModels();
      for (DataBaseModel dbConfig : dataBaseModels) {
        TreeItem<String> treeItem = new TreeItem<String>();
        treeItem.setValue(dbConfig.getConnName());
        ImageView dbImage = new ImageView("image/database.png");
        dbImage.setFitHeight(16);
        dbImage.setFitWidth(16);
        dbImage.setUserData(dbConfig);
        treeItem.setGraphic(dbImage);
        rootTreeItem.getChildren().add(treeItem);
      }
    } catch (Exception e) {
      AlertUtil.showErrorAlert(e.getMessage());
      LOG.error(e.getMessage());
    }
  }

  /**
   * @param event 全库生成
   */
  public void allGeneratedAction(ActionEvent event) {
    try {
      if (StringUtil.isNullOrEmpty(storagePath.getText())) {
        AlertUtil.showWarnAlert(Constant.ALERT_GENERATE_PATH_NULL);
        return;
      }
      if (!AlertUtil.showConfirmAlert(Constant.ALERT_GENERATE_ALL)) {
        return;
      }
      if (tables.size() == 0) {
        AlertUtil.showWarnAlert(Constant.ALERT_GENERATE_TABLE_ALL_NULL);
        return;
      }
      boolean isOverride = true;
      if (!AlertUtil.showConfirmAlert(Constant.FILE_PRESENCE_REMINDER)) {
        isOverride = false;
      }
      boolean finalIsOverride = isOverride;

      double progIndex = 1.0 / tables.size();
      probCreateAll.setVisible(true);
      Task<Void> task = new Task<Void>() {
        @Override
        protected Void call() throws Exception {
          int j = 0;
          for (int i = 0; i < tables.size(); i++) {
            updateProgress(progIndex * (i + 1), 1.0);
            updateMessage("正在生成" + " {t} ...".replace("{t}", tables.get(i)));
            boolean allRun = createAllRun(tables.get(i), finalIsOverride);
            if (allRun) {
              j++;
            }
          }
          updateMessage("共创建成功【" + j + "】张表");
          return null;
        }
      };
      probCreateAll.progressProperty().bind(task.progressProperty());
      lblRunCreateAllTips.textProperty().bind(task.messageProperty());
      new Thread(task).start();
    } catch (Exception e) {
      e.printStackTrace();
      LOG.error(e.getMessage());
    }
  }

  /**
   * @param event 选择生成
   */
  public void preGeneratedAction(ActionEvent event) {
    try {
      if (StringUtil.isNullOrEmpty(storagePath.getText())) {
        AlertUtil.showWarnAlert(Constant.ALERT_GENERATE_PATH_NULL);
        return;
      }
      String text = tableText.getText();
      if (StringUtils.isEmpty(text.trim())) {
        AlertUtil.showWarnAlert(Constant.ALERT_GENERATE_TABLE_PRE_NULL);
        return;
      }
      String[] split = text.split("\\|");
      if (!AlertUtil
        .showConfirmAlert(Constant.ALERT_GENERATE_TABLE_PRE_CONFIRM.replace("${size}", split.length + ""))) {
        return;
      }

      boolean isOverride = true;
      if (!AlertUtil.showConfirmAlert(Constant.FILE_PRESENCE_REMINDER)) {
        isOverride = false;
      }
      boolean finalIsOverride = isOverride;

      double progIndex = 1.0 / split.length;
      probCreateAll.setVisible(true);
      Task<Void> task = new Task<Void>() {
        @Override
        protected Void call() throws Exception {
          int j = 0;
          for (int i = 0; i < split.length; i++) {
            updateProgress(progIndex * (i + 1), 1.0);
            updateMessage("正在生成" + " {t} ...".replace("{t}", split[i].trim()));
            boolean allRun = createAllRun(split[i].trim(), finalIsOverride);
            if (allRun) {
              j++;
            }
          }
          updateMessage("共创建成功【" + j + "】张表");
          return null;
        }
      };
      probCreateAll.progressProperty().bind(task.progressProperty());
      lblRunCreateAllTips.textProperty().bind(task.messageProperty());
      new Thread(task).start();

    } catch (Exception e) {
      e.printStackTrace();
      LOG.error(e.getMessage());
    }
  }

  /**
   * @param event 保存配置
   */
  public void saveConfigAction(ActionEvent event) throws Exception {
    if (!AlertUtil.showConfirmAlert(Constant.ALERT_CONFIG_SAVE)) {
      return;
    }
    BaseModel baseModel = new BaseModel();
    baseModel.setStoragePath(storagePath.getText());
    baseModel.setPkgPrefix(pkgPrefix.getText());
    baseModel.setTablePrefix(tablePrefix.getText());
    baseModel.setVersion(version.getText());
    baseModel.setAuthor(author.getText());
    baseModel.setEncoding(cboCodeFormat.getValue());
    baseModel.setEntityTemp(entityComboBox.getValue());
    baseModel.setDaoTemp(daoComboBox.getValue());
    baseModel.setServiceTemp(serviceComboBox.getValue());
    baseModel.setControllerTemp(controllerComboBox.getValue());
    int i = SqlLiteUtil.saveBaseConfig(baseModel, dataBaseModel.getConnName());
  }

  private void tableText() {
    text = new ArrayList<>();
    tableText.setWrapText(true);
    tableText.setText("");
    storagePath.setText("");
    pkgPrefix.setText("");
    tablePrefix.setText("");
    module.setText("");
    author.setText("");
    version.setText("");
    cboCodeFormat.setValue("");
    entityComboBox.setValue("");
    daoComboBox.setValue("");
    serviceComboBox.setValue("");
    controllerComboBox.setValue("");
  }

  private boolean createAllRun(String tableName, boolean isOverride) {
    try {
      boolean isSuccess = false;
      GeneratorContent content = getGeneratorContent(tablePrefix.getText(), tableName);
      /** entity **/
      boolean isSuccess1 = CreateFileUtil
        .createFile(content, entityComboBox.getValue() + Constant.FTL_SUFFIX, storagePath.getText(),
                    pkgPrefix.getText(), module.getText(), tableName, tablePrefix.getText(), cboCodeFormat.getValue(),
                    isOverride
        );
      /** dao **/
      boolean isSuccess2 = CreateFileUtil
        .createFile(content, daoComboBox.getValue() + Constant.FTL_SUFFIX, storagePath.getText(), pkgPrefix.getText(),
                    module.getText(), tableName, tablePrefix.getText(), cboCodeFormat.getValue(), isOverride
        );
      /** service **/
      boolean isSuccess3 = CreateFileUtil
        .createFile(content, serviceComboBox.getValue() + Constant.FTL_SUFFIX, storagePath.getText(),
                    pkgPrefix.getText(), module.getText(), tableName, tablePrefix.getText(), cboCodeFormat.getValue(),
                    isOverride
        );
      /** controller **/
      boolean isSuccess4 = CreateFileUtil
        .createFile(content, controllerComboBox.getValue() + Constant.FTL_SUFFIX, storagePath.getText(),
                    pkgPrefix.getText(), module.getText(), tableName, tablePrefix.getText(), cboCodeFormat.getValue(),
                    isOverride
        );
      if (isSuccess1 || isSuccess2 || isSuccess3 || isSuccess4) {
        isSuccess = true;
      }
      return isSuccess;
    } catch (Exception e) {
      e.printStackTrace();
      LOG.error(e.getMessage());
      return false;
    }
  }

  public GeneratorContent getGeneratorContent(String tablePrefix, String tableName) {
    GeneratorContent content = new GeneratorContent();
    try {
      String classVar = StringUtil
        .toBeanPatternStr(tableName.startsWith(tablePrefix) ? tableName.substring(tablePrefix.length()) : tableName);


      HistoryModel history = getThisHistoryConfigAndInit(dataBaseModel, tableName);
      // 数据库属性
      DatabaseContent databaseContent = new DatabaseContent();
      ConverterUtil.databaseConfigToContent(dataBaseModel, databaseContent);
      content.setDatabase(databaseContent);

      // 数据库表属性
      TableContent tableContent = DBUtil.getTableAttribute(dataBaseModel, tableName);
      content.setTable(tableContent);

      // 基础属性
      BaseContent baseContent = new BaseContent(pkgPrefix.getText(),
                                                StringUtils.isEmpty(module.getText()) ? null : module.getText().trim(),
                                                StringUtils.isEmpty(author.getText()) ? null : author.getText().trim(),
                                                StringUtils.isEmpty(version.getText())
                                                ? "1.0.0"
                                                : version.getText().trim()
      );
      content.setBase(baseContent);

      // entity属性
      EntityModel ec = getThisHistoryConfigAndInit(dataBaseModel, tableName).getEntityConfig();
      String className = StringUtil.firstCharUpperCase(classVar);
      String entityName = StringUtil.firstCharUpperCase(classVar);
      EntityContent entityContent = new EntityContent(
        pkgPrefix.getText() + "." + Constant.FILE_ENTITY.toLowerCase(), entityName, tableName);
      ConverterUtil.entityConfigToContent(ec, entityContent);
      content.setEntity(entityContent);

      // Service属性
      ServiceModel sc = history.getServiceConfig();
      String serviceName = StringUtil.firstCharUpperCase(classVar) + Constant.FILE_SERVICE;
      ServiceContent serviceContent = new ServiceContent(
        pkgPrefix.getText() + "." + Constant.FILE_SERVICE.toLowerCase(), serviceName);
      content.setService(serviceContent);

      // dao属性
      String sqlName = StringUtil.firstCharUpperCase(classVar) + Constant.FILE_DAO;
      DaoModel sql = history.getDaoConfig();
      DaoContent sqlContent = new DaoContent(pkgPrefix.getText() + "." + Constant.FILE_DAO.toLowerCase(), sqlName);
      content.setDao(sqlContent);

      // Controller属性
      String routerName = StringUtil.firstCharUpperCase(classVar) + Constant.FILE_CONTROLLER;
      ControllerModel router = history.getControllerConfig();
      ControllerContent routerContent = new ControllerContent(
        pkgPrefix.getText() + "." + Constant.FILE_CONTROLLER.toLowerCase(), routerName);
      content.setController(routerContent);
    } catch (Exception e) {
      e.printStackTrace();
      LOG.error(e.getMessage());
    }

    return content;
  }

  private HistoryModel getThisHistoryConfigAndInit(DataBaseModel databaseConfig, String selectedTableName) {
    try {
      HistoryModel config = getThisHistoryConfig();
      if (config.getEntityConfig() == null) {
        EntityModel entityConfig = Optional.ofNullable(SqlLiteUtil.getEntityConfig(Constant.DEFAULT))
                                           .orElse(new EntityModel());
        List<TableAttributeEntity> columns = DBUtil.getTableColumns(databaseConfig, selectedTableName);
        if (entityConfig.isFieldCamel()) {
          for (TableAttributeEntity attr : columns) {
            attr.setTdField(StringUtil.unlineToCamel(attr.getTdColumnName()));
          }
        } else {
          for (TableAttributeEntity attr : columns) {
            attr.setTdField(attr.getTdColumnName());
          }
        }
        entityConfig.setTblPropertyValues(FXCollections.observableArrayList(columns));
        String primaryKey = DBUtil.getTablePrimaryKey(databaseConfig, selectedTableName);
        entityConfig.setPrimaryKey(primaryKey);
        config.setEntityConfig(entityConfig);
      }

      return config;
    } catch (Exception e) {
      e.printStackTrace();
      LOG.error(e.getMessage());
    }
    return null;
  }

  private HistoryModel getThisHistoryConfig() {
    HistoryModel historyModel = new HistoryModel();

    return historyModel;
  }

  /**
   * 选择项目文件
   *
   * @param event
   */
  public void onSelectProjectPath(ActionEvent event) {
    DirectoryChooser directoryChooser = new DirectoryChooser();
    File file = directoryChooser.showDialog(this.getPrimaryStage());
    if (file != null) {
      storagePath.setText(file.getPath());
    }
  }

  /**
   * 选择项目文件
   *
   * @param event
   */
  public void openProjectPath(ActionEvent event) {
    try {
      Desktop.getDesktop().open(new File(storagePath.getText()));
    } catch (Exception e) {
      AlertUtil.showWarnAlert(Constant.PATH_ERROR);
      LOG.error(e.getMessage());
    }
  }

}
